home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1994 August: Tool Chest / Dev.CD Aug 94.toast / Sample Code / AppsToGo / DTS.Lib / TreeObj.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-24  |  60.6 KB  |  2,450 lines  |  [TEXT/MPS ]

  1. /*
  2. ** Apple Macintosh Developer Technical Support
  3. **
  4. ** Program:     DTS.Lib
  5. ** File:        TreeObj.c
  6. ** Written by:  Eric Soldan
  7. **
  8. ** Copyright © 1992-1993 Apple Computer, Inc.
  9. ** All rights reserved.
  10. */
  11.  
  12. /* You may incorporate this sample code into your applications without
  13. ** restriction, though the sample code has been provided "AS IS" and the
  14. ** responsibility for its operation is 100% yours.  However, what you are
  15. ** not permitted to do is to redistribute the source as "DSC Sample Code"
  16. ** after having made changes. If you're going to re-distribute the source,
  17. ** we require that you make it clear in the source that the code was
  18. ** descended from Apple Sample Code, but that you've made changes. */
  19.  
  20. #include "DTS.Lib2.h"
  21. #include "DTS.Lib.protos.h"
  22.  
  23. #ifndef __FILES__
  24. #include <Files.h>
  25. #endif
  26.  
  27. //#ifndef __STDIO__
  28. //#include <StdIO.h>
  29. //#endif
  30.  
  31. //#ifndef __STRING__
  32. //#include <String.h>
  33. //#endif
  34.  
  35. #ifndef __TREEOBJ__
  36. #include "TreeObj.h"
  37. #endif
  38.  
  39.  
  40.  
  41. #define NEW_CHILD     1
  42. #define DISPOSE_CHILD 2
  43. #define MOVE_CHILD    3
  44. #define SWAP_CHILDREN 4
  45. #define CHANGE_CHILD  5
  46.  
  47.  
  48.  
  49. extern TreeObjProcPtr    gTreeObjMethods[];
  50. extern long                gMinTreeObjSize[];
  51.  
  52. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copyCNum);
  53. static OSErr        ReadBranch(TreeObjHndl hndl, short fileRefNum);
  54. static OSErr        ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  55. static OSErr        WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum);
  56.  
  57. static void            DoNumberTree0(TreeObjHndl hndl);
  58. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType);
  59. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short cnum, TreeObjHndl dhndl, short dcnum, Boolean deepCopy);
  60.  
  61. static OSErr        PostNewChild(short editType, TreeObjHndl phndl, short cnum);
  62. static OSErr        PostDisposeChild(short editType, TreeObjHndl phndl, short cnum);
  63. static void            PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum);
  64. static void            PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb);
  65.  
  66. static void            UndoNewChild(TreeObjHndl undoPart);
  67. static void            UndoDisposeChild(TreeObjHndl undoPart);
  68. static void            UndoMoveChild(TreeObjHndl undoPart);
  69. static void            UndoModifyChild(TreeObjHndl undoPart);
  70. static void            UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy);
  71. static void            UndoSwapChildren(TreeObjHndl undoPart);
  72.  
  73.  
  74.  
  75. /**********************************************************************/
  76.  
  77.  
  78.  
  79. /* Creates an object with no parent.  This object will be the root object
  80. ** for a tree.  Returns nil upon failure. */
  81.  
  82. #pragma segment TreeObj
  83. TreeObjHndl    NewRootObj(short ctype, long size)
  84. {
  85.     TreeObjHndl        root;
  86.     TreeObjPtr        ptr;
  87.     char            *cptr;
  88.     short            i;
  89.     TreeObjProcPtr    proc;
  90.  
  91.     if (size < gMinTreeObjSize[ctype])
  92.         size = gMinTreeObjSize[ctype];
  93.             /* Ensure a minimally-sized object. */
  94.  
  95.     root = (TreeObjHndl)NewHandle(sizeof(TreeObj) + size);
  96.     if (!root) return(nil);
  97.         /* Oh well...
  98.         ** Note that since we are creating an orphan, it isn't possible to purge
  99.         ** old undos to possibly make space.  This means that for anyone calling
  100.         ** NewRootObj to get a handlewho considers it okay to purge old undos to
  101.         ** get the memory if necessary, has to do the following:
  102.         ** 1)  Call NewRootObj
  103.         ** 2)  If failure, call PurgeUndo for a particular document to purge the
  104.         **     oldest undo for that document to free up ram.
  105.         ** 3)  If PurgeUndo returns true, then something was purged and there is more
  106.         **     ram available.  Loop back to step 1 and try to create the object again.
  107.         ** 4)  If PurgeUndo returns false, then there really isn't any more ram.  One
  108.         **     possibility is to try purging undos from other documents, but this is
  109.         **     probably rude. */
  110.  
  111.     cptr = GetDataPtr(root);
  112.     for (i = 0; i < size; ++i) cptr[i] = 0;
  113.         /* Initialize data area to 0's.  This is very nice of us. */
  114.  
  115.     ptr = *root;                     /* Deref root object once.      */
  116.     ptr->type        = ctype;         /* Flag what object type it is. */
  117.     ptr->numChildren = 0;            /* Give root 0 children.        */
  118.     ptr->dataSize    = size;        /* Remember the data area size. */
  119.     ptr->treeID      = 0;            /* Requisite do-nothing field.  */
  120.     ptr->parent      = nil;            /* We aren't owned yet.         */
  121.  
  122.     proc = gTreeObjMethods[ctype];
  123.     if (proc) {                                            /* If this object type has a proc...   */
  124.         if ((*proc)(root, INITMESSAGE, CREATEINIT)) {    /* Call the proc with an init message. */
  125.             DisposeHandle((Handle)root);                /* If the init complains, it's no go.  */
  126.             return(nil);
  127.         }
  128.     }
  129.  
  130.     return(root);    /* Success.  Return the handle. */
  131. }
  132.  
  133.  
  134.  
  135. /**********************************************************************/
  136.  
  137.  
  138.  
  139. /* Creates a child of the specified type and adds it to the parent at the
  140. ** specified location.  Returns nil upon failure. */
  141.  
  142. #pragma segment TreeObj
  143. TreeObjHndl    NewChild(short editType, TreeObjHndl phndl, short cnum, short ctype, long size)
  144. {
  145.     TreeObjHndl        chndl;
  146.     TreeObjProcPtr    proc;
  147.  
  148.     for (;;) {
  149.         chndl = NewRootObj(ctype, size);    /* Let somebody else do the creative work. */
  150.         if (chndl) break;                    /* We succeeded at creating an orphan. */
  151.         if (!PurgeUndo(phndl)) return(nil);
  152.             /* Oh well...
  153.             ** There is really no memory.  We even purged the undos to try to make
  154.             ** room, but we still didn't have enough ram to create the orphan object. */
  155.     };
  156.  
  157.     /* Now that we have successfully created an orphan object, make it someone's child. */
  158.  
  159.     GetChildHndlPtr(phndl, &cnum, 1);
  160.         /* Adjust cnum to within bounds.  See GetChildHndlPtr() for more info. */
  161.  
  162.     if (InsertChildHndl(phndl, chndl, cnum)) {
  163.             /* Couldn't insert the child into the parent handle table. */
  164.         proc = gTreeObjMethods[ctype];
  165.         if (proc)
  166.             (*proc)(chndl, FREEMESSAGE, 0);
  167.                 /* Since we couldn't attatch, call the object with a free message so that
  168.                 ** it can get rid of any additional memory that was allocated when an
  169.                 ** init message was sent to it. */
  170.         DisposeHandle((Handle)chndl);
  171.             /* After the free message, the object now consists of a single handle.
  172.             ** Dispose this handle and it is history completely. */
  173.         return(nil);
  174.     }
  175.  
  176.     if (PostNewChild(editType, phndl, cnum)) {
  177.         DisposeChild(NO_EDIT, phndl, cnum);
  178.         chndl = nil;
  179.     }
  180.  
  181.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  182.     return(chndl);
  183. }
  184.  
  185.  
  186.  
  187. /**********************************************************************/
  188.  
  189.  
  190.  
  191. /* Disposes of the specified child and all offspring of that child. */
  192.  
  193. #pragma segment TreeObj
  194. void    DisposeChild(short editType, TreeObjHndl phndl, short cnum)
  195. {
  196.     TreeObjHndl    chndl;
  197.  
  198.     if (GetChildHndlPtr(phndl, &cnum, 0)) {
  199.         /* This test checks that there are children in the table, plus it
  200.         ** also adjusts cnum to legit values, if possible. */
  201.  
  202.         chndl = GetChildHndl(phndl, cnum);
  203.  
  204.         if (editType) {
  205.             if (PostDisposeChild(editType, phndl, cnum)) {
  206.                     /* Posting a DisposeChild can actually take more ram.
  207.                     ** The reason for this is that the object isn't disposed of.
  208.                     ** It is moved into the undo, plus information as to where to
  209.                     ** put it back is kept.  If PostDisposeChild fails, then we
  210.                     ** are in a bad way memory-wise, so we really need to let the
  211.                     ** dispose occur.  To accomplish this, we disable undos for
  212.                     ** this document, and then we go ahead and allow a straight
  213.                     ** dispose of the child.  This will get the job done, plus it
  214.                     ** will free up some ram.
  215.                     ** The reason that undos are disabled is that the edit that
  216.                     ** is occuring may have multiple operations.  We want to stop
  217.                     ** collection of the remaining operations for this edit, as
  218.                     ** it wouldn't be a complete undo anyway.  The undos will
  219.                     ** be enabled again when NewUndo is called to start a new edit. */
  220.                 DisableUndo(phndl);
  221.                     /* Undo collection off temporarily, plus all undos are purged,
  222.                     ** thus freeing up ram. */
  223.                 editType = 0;
  224.             }
  225.         }
  226.  
  227.         if (!editType) {
  228.             DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  229.                 /* If the dispose was successfully posted, then the object got a message that
  230.                 ** it was leaving the document via PostDisposeChild calling MoveChild.  The
  231.                 ** only way that PostDisposeChild can fail (and therefore not call MoveChild)
  232.                 ** is if an undoTask couldn't be created, and this is prior to MoveChild being
  233.                 ** called.  What all this means is that if we are here, the object hadn't yet
  234.                 ** received a message about leaving the document. */
  235.             DisposeObjAndOffspring(GetChildHndl(phndl, cnum));
  236.             *GetChildHndlPtr(phndl, &cnum, 0) = nil;
  237.                 /* Since the child no longer exists, DeleteChildHndl can't set
  238.                 ** the parent reference for the child to nil.  Setting the reference
  239.                 ** to the child prevents DeleteChildHndl from doing this. */
  240.             DeleteChildHndl(phndl, cnum);
  241.         }
  242.     }
  243. }
  244.  
  245.  
  246.  
  247. /**********************************************************************/
  248.  
  249.  
  250.  
  251. /* Copies the specified child (and all offspring of that child if deepCopy is true). */
  252.  
  253. #pragma segment TreeObj
  254. TreeObjHndl    CopyChild(short editType, TreeObjHndl shndl, short scnum,
  255.                       TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  256. {
  257.     TreeObjHndl    chndl, copyHndl;
  258.  
  259.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(nil);
  260.         /* Adjust scnum to within bounds, if possible. */
  261.  
  262.     GetChildHndlPtr(dhndl, &dcnum, 1);
  263.         /* Adjust dcnum to within bounds. */
  264.  
  265.     copyHndl = CopyOneChild(chndl = GetChildHndl(shndl, scnum), dhndl, dcnum);
  266.     if (!copyHndl) return(nil);
  267.  
  268.     if (deepCopy) {
  269.         if (CopyChildren(chndl, copyHndl)) {        /* Copy child's children. */
  270.             DisposeChild(NO_EDIT, dhndl, dcnum);
  271.             return(nil);
  272.         }
  273.     }
  274.  
  275.     if (PostNewChild(editType, dhndl, dcnum)) {
  276.         DisposeChild(NO_EDIT, dhndl, dcnum);
  277.         return(nil);
  278.     }
  279.  
  280.     DoTreeObjMethod(copyHndl, UNDOMESSAGE, UNDOTODOC);
  281.     return(copyHndl);
  282. }
  283.  
  284.  
  285.  
  286. /**********************************************************************/
  287.  
  288.  
  289.  
  290. /* Moves a child from one location on the tree to another. */
  291.  
  292. #pragma segment TreeObj
  293. OSErr    MoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  294. {
  295.     TreeObjHndl    chndl;
  296.  
  297.     if (!GetChildHndlPtr(shndl, &scnum, 0)) return(paramErr);
  298.         /* Adjust scnum to within bounds.  Adjustment not possible if no children. */
  299.  
  300.     GetChildHndlPtr(dhndl, &dcnum, 1);        /* Adjust dcnum to within bounds. */
  301.  
  302.     chndl = GetChildHndl(shndl, scnum);
  303.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOFROMDOC);
  304.     DeleteChildHndl(shndl, scnum);
  305.  
  306.     GetChildHndlPtr(dhndl, &dcnum, 1);
  307.         /* Adjust dcnum to within bounds.  It may be just out of bounds if shndl
  308.         ** and dhndl are the same.  After the DeleteChildHndl, dcnum may be 2 past
  309.         ** the end, instead of the allowable 1 for a target. */
  310.  
  311.     InsertChildHndl(dhndl, chndl, dcnum);
  312.     DoTreeObjMethod(chndl, UNDOMESSAGE, UNDOTODOC);
  313.  
  314.     PostMoveChild(editType, shndl, scnum, dhndl, dcnum);
  315.     return(noErr);
  316. }
  317.  
  318.  
  319.  
  320. /**********************************************************************/
  321.  
  322.  
  323.  
  324. /* Saves a copy of the child in the undo hierarchy for undo/redo purposes. */
  325.  
  326. #pragma segment TreeObj
  327. OSErr    ModifyChild(short editType, TreeObjHndl phndl, short cnum, Boolean deepCopy)
  328. {
  329.     TreeObjHndl    undo, task, part, copy;
  330.     short        i;
  331.     OSErr        err;
  332.  
  333.     if (!editType) return(noErr);
  334.  
  335.     err  = noErr;
  336.     copy = nil;
  337.     undo = GetUndoHndl(phndl);
  338.  
  339.     if (mDerefUndo(undo)->disabled)
  340.         copy = phndl;        /* Used for flag purposes. */
  341.  
  342.     else {
  343.         task = GetUndoTaskHndl(undo, editType);
  344.         if (task) {
  345.             for (i = (*task)->numChildren; i;) {
  346.                 part = GetChildHndl(task, --i);
  347.                 if (mDerefUndoPart(part)->actionType == CHANGE_CHILD)
  348.                     if ((mDerefUndoPart(part)->shndl ==phndl) && (mDerefUndoPart(part)->scnum ==cnum))
  349.                         return(noErr);
  350.                             /* Child is already posted as changed for this undo editType. */
  351.             }
  352.  
  353.             part = NewUndoPart(task, CHANGE_CHILD, phndl, cnum, nil, 0, deepCopy);
  354.             if (part)
  355.                 copy = CopyChild(NO_EDIT, phndl, cnum, part, -1, deepCopy);
  356.         }
  357.     }
  358.  
  359.     if (!copy) {
  360.         DisableUndo(phndl);
  361.         err = memFullErr;
  362.     }
  363.  
  364.     return(err);
  365. }
  366.  
  367.  
  368.  
  369. /**********************************************************************/
  370.  
  371.  
  372.  
  373. /* Swaps the child handles. */
  374.  
  375. #pragma segment TreeObj
  376. OSErr    SwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  377. {
  378.     TreeObjHndl    *tptra, *tptrb, hndl;
  379.  
  380.     if (!(tptra = GetChildHndlPtr(hndla, &cnuma, 0))) return(paramErr);
  381.     if (!(tptrb = GetChildHndlPtr(hndlb, &cnumb, 0))) return(paramErr);
  382.  
  383.     BlockMove(tptra, &hndl, sizeof(TreeObjHndl));
  384.     BlockMove(tptrb, tptra, sizeof(TreeObjHndl));
  385.     BlockMove(&hndl, tptra, sizeof(TreeObjHndl));
  386.  
  387.     (*GetChildHndl(hndla, cnuma))->parent = hndla;
  388.     (*GetChildHndl(hndlb, cnumb))->parent = hndlb;
  389.  
  390.     PostSwapChildren(editType, hndla, cnuma, hndlb, cnumb);
  391.     return(noErr);
  392. }
  393.  
  394.  
  395.  
  396. /**********************************************************************/
  397.  
  398.  
  399.  
  400. /* Swaps the child data without swapping the handles. */
  401.  
  402. #pragma segment TreeObj
  403. OSErr    SwapTreeObjData(TreeObjHndl hndla, TreeObjHndl hndlb)
  404. {
  405.     TreeObjHndl    hndl;
  406.     long        size, sizea, sizeb, i;
  407.     char        *ptra, *ptrb, c;
  408.     OSErr        err;
  409.  
  410.     sizea = (*hndla)->dataSize;
  411.     sizeb = (*hndlb)->dataSize;
  412.     if (sizea > sizeb) {        /* Make hndla the small one and hndlb the big one. */
  413.         size  = sizea;
  414.         sizea = sizeb;
  415.         sizeb = size;
  416.         hndl  = hndla;
  417.         hndla = hndlb;
  418.         hndlb = hndl;
  419.     }
  420.  
  421.     err = SetDataSize(hndla, sizeb);        /* Make the small one big. */
  422.     if (!err) {
  423.         ptra = GetDataPtr(hndla);
  424.         ptrb = GetDataPtr(hndlb);
  425.         for (i = 0; i < sizeb; ++i) {
  426.             c = ptra[i];
  427.             ptra[i] = ptrb[i];
  428.             ptrb[i] = c;
  429.         }
  430.         SetDataSize(hndlb, sizea);            /* Make the big one small. */
  431.     }
  432.  
  433.     return(err);
  434. }
  435.  
  436.  
  437.  
  438. /**********************************************************************/
  439. /**********************************************************************/
  440.  
  441.  
  442.  
  443. /* This disposes of the child and any offspring of that child.  It does not remove
  444. ** the child from the parent's child handle table. */
  445.  
  446. #pragma segment TreeObj
  447. void    DisposeObjAndOffspring(TreeObjHndl chndl)
  448. {
  449.     short            nc;
  450.     TreeObjProcPtr    proc;
  451.  
  452.     if (!chndl) return;
  453.  
  454.     proc = gTreeObjMethods[(*chndl)->type];
  455.     if (proc)
  456.         (*proc)(chndl, FREEMESSAGE, 0);
  457.             /* If the object has any additional deallocation to do, let it do it. */
  458.  
  459.     while ((nc = (*chndl)->numChildren) != 0) {
  460.         DisposeObjAndOffspring(GetChildHndl(chndl, nc - 1));
  461.         (*chndl)->numChildren--;
  462.     }
  463.     DisposeHandle((Handle)chndl);
  464. }
  465.  
  466.  
  467.  
  468. /**********************************************************************/
  469.  
  470.  
  471.  
  472. /* Copies the children (and children below that and so on) of one object to
  473. ** another object.  Used internally by CopyChild for deep copies. */
  474.  
  475. #pragma segment TreeObj
  476. OSErr    CopyChildren(TreeObjHndl shndl, TreeObjHndl dhndl)
  477. {
  478.     TreeObjHndl    chndl, copyHndl;
  479.     short    i;
  480.     OSErr    err;
  481.  
  482.     for (i = (*shndl)->numChildren; i;) {
  483.         chndl    = GetChildHndl(shndl, --i);
  484.         copyHndl = CopyOneChild(chndl, dhndl, 0);
  485.         if (!copyHndl) return(memFullErr);
  486.         err = CopyChildren(chndl, copyHndl);
  487.         if (err) return(err);
  488.     }
  489.     return(noErr);
  490. }
  491.  
  492.  
  493.  
  494. /**********************************************************************/
  495.  
  496.  
  497.  
  498. /* Used internally by CopyChild for shallow copies. */
  499.  
  500. #pragma segment TreeObj
  501. static TreeObjHndl    CopyOneChild(TreeObjHndl chndl, TreeObjHndl copyToHndl, short copycnum)
  502. {
  503.     TreeObjHndl        copyHndl;
  504.     short            type;
  505.     long            size;
  506.     TreeObjProcPtr    proc;
  507.  
  508.     type = (*chndl)->type;
  509.     size = (*chndl)->dataSize;
  510.  
  511.  
  512.     proc = gTreeObjMethods[type];    /* Prevent NewChild() from calling the */
  513.     gTreeObjMethods[type] = nil;    /* object for INITMESSAGE. */
  514.  
  515.     copyHndl = NewChild(NO_EDIT, copyToHndl, copycnum, type, size);
  516.         /* NewChild takes care of bounds-checking for copycnum.  The child data has
  517.         ** not been fully initialized, as we prevented the INITMESSAGE. */
  518.  
  519.     gTreeObjMethods[type] = proc;    /* Re-enable messaging the object. */
  520.  
  521.     if (!copyHndl) return(nil);
  522.  
  523.     BlockMove(GetDataPtr(chndl), GetDataPtr(copyHndl), size);
  524.     if (proc) {
  525.         if ((*proc)(copyHndl, COPYMESSAGE, (long)chndl)) {
  526.             DisposeChild(NO_EDIT, copyHndl, copycnum);
  527.             return(nil);
  528.         }
  529.     }
  530.  
  531.     return(copyHndl);
  532. }
  533.  
  534.  
  535.  
  536. /**********************************************************************/
  537. /**********************************************************************/
  538.  
  539.  
  540.  
  541. /* Adds an existing child to a parent's child handle table. */
  542.  
  543. #pragma segment TreeObj
  544. OSErr    InsertChildHndl(TreeObjHndl phndl, TreeObjHndl chndl, short cnum)
  545. {
  546.     TreeObjHndl    *tptr;
  547.     TreeObjPtr    ptr;
  548.     long        oldSize, newSize, dhSize;
  549.     long        oldTblSize, tblOffset;
  550.     OSErr        err;
  551.  
  552.     oldSize = GetHandleSize((Handle)phndl);
  553.     dhSize  = sizeof(TreeObj) + (ptr = *phndl)->dataSize;        /* Data + header size. */
  554.  
  555.     oldTblSize = ptr->numChildren * sizeof(TreeObjHndl);
  556.  
  557.     newSize = dhSize + oldTblSize + sizeof(TreeObjHndl);
  558.     newSize |= 0x1F;
  559.     ++newSize;
  560.  
  561.     if (newSize > oldSize) {
  562.         SetHandleSize((Handle)phndl, newSize);
  563.         err = MemError();
  564.         if (err) return(err);
  565.         ptr = *phndl;
  566.     }
  567.  
  568.     tptr      = GetChildHndlPtr(phndl, &cnum, 1);
  569.     tblOffset = cnum * sizeof(TreeObjHndl);
  570.     BlockMove((char *)tptr, (char *)(tptr + 1), oldTblSize - tblOffset);
  571.  
  572.     ptr->numChildren++;
  573.     BlockMove(&chndl, tptr, sizeof(TreeObjHndl));
  574.     (*chndl)->parent = phndl;
  575.     return(noErr);
  576. }
  577.  
  578.  
  579.  
  580. /**********************************************************************/
  581.  
  582.  
  583.  
  584. /* Removes a child from the parent's child handle table. */
  585.  
  586. #pragma segment TreeObj
  587. void    DeleteChildHndl(TreeObjHndl phndl, short cnum)
  588. {
  589.     TreeObjHndl    *tptr;
  590.     TreeObjHndl    chndl;
  591.     TreeObjPtr    ptr;
  592.     long        dhSize, tblSize, tblOffset;
  593.  
  594.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return;
  595.     BlockMove(tptr, &chndl, sizeof(TreeObjHndl));
  596.  
  597.     dhSize    = sizeof(TreeObj) + (ptr = *phndl)->dataSize;
  598.     tblSize   = ptr->numChildren * sizeof(TreeObjHndl);
  599.     tblOffset = cnum * sizeof(TreeObjHndl);
  600.  
  601.     BlockMove((char *)(tptr + 1), (char *)tptr, tblSize - tblOffset - sizeof(TreeObjHndl));
  602.     SetHandleSize((Handle)phndl, ((dhSize + tblSize - sizeof(TreeObjHndl)) | 0x1F) + 1);
  603.     (*phndl)->numChildren--;
  604.  
  605.     if (chndl)
  606.         (*chndl)->parent = nil;
  607. }
  608.  
  609.  
  610.  
  611. /**********************************************************************/
  612.  
  613.  
  614.  
  615. /* Given an object handle, return the root handle. */
  616.  
  617. #pragma segment TreeObj
  618. TreeObjHndl    GetRootHndl(TreeObjHndl hndl)
  619. {
  620.     for (; (*hndl)->parent; hndl = (*hndl)->parent) {};
  621.     return(hndl);
  622. }
  623.  
  624.  
  625.  
  626. /**********************************************************************/
  627.  
  628.  
  629.  
  630. /* Given a parent handle and a child number, this returns the child handle. */
  631.  
  632. #pragma segment TreeObj
  633. TreeObjHndl    GetChildHndl(TreeObjHndl phndl, short cnum)
  634. {
  635.     TreeObjHndl    *tptr, th;
  636.  
  637.     if (!(tptr = GetChildHndlPtr(phndl, &cnum, 0))) return(nil);
  638.     BlockMove(tptr, &th, sizeof(TreeObjHndl));
  639.     return(th);
  640. }
  641.  
  642.  
  643.  
  644. /**********************************************************************/
  645.  
  646.  
  647.  
  648. /* Return a pointer into the child handle table.  This also validates (and corrects)
  649. ** cnum so that it is in range, if possible.  Depending on the usage, pointing to
  650. ** just after the child handle table is either valid or invalid.  If a handle is
  651. ** being added to the table, then pointing just after the table is valid.  If a
  652. ** handle is being removed or referenced, then pointing just after the table is
  653. ** invalid.  the parameter endCase determines which case we are dealing with.  If
  654. ** endCase is 0, then pointing just after the child handle table is invalid, and
  655. ** if the cnum value passed in causes this, then nil is returned for the pointer.
  656. ** if endCase is 1, then pointing just after the child handle table is okay, and
  657. ** therefore nil will never be returned as the pointer.  Any cnum value out of
  658. ** range will be corrected (if possible) to be within range. */
  659.  
  660. #pragma segment TreeObj
  661. TreeObjHndl    *GetChildHndlPtr(TreeObjHndl phndl, short *cnum, short endCase)
  662. {
  663.     TreeObjPtr    ptr;
  664.     short        nc;
  665.     long        dhSize, tblOffset;
  666.  
  667.     ptr = *phndl;
  668.     if (!((nc = ptr->numChildren) + endCase)) return(nil);
  669.  
  670.     if ((*cnum < 0) || (*cnum >= nc + endCase))
  671.         *cnum = nc - 1 + endCase;
  672.  
  673.     dhSize    = sizeof(TreeObj) + ptr->dataSize;
  674.     tblOffset = *cnum * sizeof(TreeObjHndl);
  675.     return((TreeObjHndl *)((char *)ptr + dhSize + tblOffset));
  676. }
  677.  
  678.  
  679.  
  680. /**********************************************************************/
  681.  
  682.  
  683.  
  684. /* Given a child handle, this returns the child number of that
  685. ** handle in the parent's child handle table. */
  686.  
  687. #pragma segment TreeObj
  688. short    GetChildNum(TreeObjHndl hndl)
  689. {
  690.     TreeObjHndl    phndl, *tptr, th;
  691.     short        nc, j;
  692.  
  693.     if (!(phndl = (*hndl)->parent)) return(-1);
  694.         /* Child doesn't have a parent, and therefore it is a root. */
  695.  
  696.     j = 0;
  697.     tptr = GetChildHndlPtr(phndl, &j, 0);
  698.     if (tptr) {
  699.         nc  = (*phndl)->numChildren;
  700.         for (; j < nc; ++j) {
  701.             BlockMove(tptr++, &th, sizeof(TreeObjHndl));
  702.             if (th == hndl) return(j);
  703.         }
  704.     }
  705.  
  706.     return(-1);
  707. }
  708.  
  709.  
  710.  
  711. /**********************************************************************/
  712. /**********************************************************************/
  713.  
  714.  
  715.  
  716. /* Adjusts the data size, either greater or smaller, depending on the sign of 'delta'. */
  717.  
  718. #pragma segment TreeObj
  719. OSErr    AdjustDataSize(TreeObjHndl hndl, long delta)
  720. {
  721.     TreeObjPtr    ptr;
  722.     long        dhSize, tblSize;
  723.     char        *cptr;
  724.     OSErr        err;
  725.  
  726.     ptr = *hndl;
  727.     tblSize = ptr->numChildren * sizeof (TreeObjHndl);
  728.     dhSize  = sizeof(TreeObj) + ptr->dataSize;
  729.  
  730.     if (!(delta & 0x80000000L)) {
  731.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  732.         err = MemError();
  733.         if (err) return(err);
  734.         ptr = *hndl;
  735.     }
  736.  
  737.     cptr = (char *)ptr + dhSize;
  738.     BlockMove(cptr, cptr + delta, tblSize);
  739.     ptr->dataSize += delta;
  740.  
  741.     if (delta & 0x80000000L)
  742.         SetHandleSize((Handle)hndl, ((dhSize + tblSize + delta) | 0x1F) + 1);
  743.  
  744.     return(noErr);
  745. }
  746.  
  747.  
  748.  
  749. /**********************************************************************/
  750.  
  751.  
  752.  
  753. /* Sets the data size to newSize. */
  754.  
  755. #pragma segment TreeObj
  756. OSErr    SetDataSize(TreeObjHndl hndl, long newSize)
  757. {
  758.     return(AdjustDataSize(hndl, newSize - (*hndl)->dataSize));
  759. }
  760.  
  761.  
  762.  
  763. /**********************************************************************/
  764.  
  765.  
  766.  
  767. /* Slides some of the data in the data portion of the object starting at
  768. ** 'offset' by a 'delta' amount, either forward or backward, depending
  769. ** on the sign of 'delta'. */
  770.  
  771. #pragma segment TreeObj
  772. OSErr    SlideData(TreeObjHndl hndl, long offset, long delta)
  773. {
  774.     long    dataSize;
  775.     char    *cptr;
  776.     OSErr    err;
  777.  
  778.     dataSize = (*hndl)->dataSize;
  779.  
  780.     if (!(delta & 0x80000000L)) {
  781.         err = AdjustDataSize(hndl, delta);
  782.         if (err) return(err);
  783.     }
  784.  
  785.     cptr = (char *)GetDataPtr(hndl) + offset;
  786.     BlockMove(cptr, cptr + delta, dataSize - offset);
  787.  
  788.     if (delta & 0x80000000L)
  789.         AdjustDataSize(hndl, delta);
  790.  
  791.     return(noErr);
  792. }
  793.  
  794.  
  795.  
  796. /**********************************************************************/
  797.  
  798.  
  799.  
  800. /* Returns a pointer to the beginning of the object's data area.
  801. ** THIS DOES NOT LOCK THE HANDLE!!  The pointer may become invalid
  802. ** if memory moves. */
  803.  
  804. #pragma segment TreeObj
  805. void    *GetDataPtr(TreeObjHndl hndl)
  806. {
  807.     return(*hndl + 1);
  808. }
  809.  
  810.  
  811.  
  812. /**********************************************************************/
  813. /**********************************************************************/
  814.  
  815.  
  816.  
  817. /* Given an open file that has been previously written via WriteTree, this function
  818. ** is called to read the file data into ram.  The root object for the file is already
  819. ** created by InitDocument.  The file must be open, and the file position must be
  820. ** set to the byte location where the root object of the file starts.  Once this is
  821. ** so, just call this function with a reference to the root object and the open file
  822. ** refrence number. */
  823.  
  824. #pragma segment TreeObj
  825. OSErr    ReadTree(TreeObjHndl hndl, short fileRefNum)
  826. {
  827.     OSErr    err;
  828.  
  829.     err = ReadBranch(hndl, fileRefNum);
  830.     if (err) {
  831.         while ((*hndl)->numChildren)
  832.             DisposeChild(NO_EDIT, hndl, 0);
  833.     }
  834.     else {
  835.         DoNumberTree(hndl);
  836.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  837.     }
  838.  
  839.     return(err);
  840. }
  841.  
  842.  
  843.  
  844. /**********************************************************************/
  845.  
  846.  
  847.  
  848. /* This is an internal function for recursively reading the data from the
  849. ** open file. */
  850.  
  851. #pragma segment TreeObj
  852. static OSErr    ReadBranch(TreeObjHndl hndl, short fileRefNum)
  853. {
  854.     TreeObjHndl        chndl;
  855.     TreeObjProcPtr    proc;
  856.     short            numChildren, cnum;
  857.     OSErr            err;
  858.  
  859.     err = ReadTreeObjHeader(hndl, fileRefNum);
  860.     if (err) {
  861.         (*hndl)->numChildren = 0;    /* So we can dispose the portion that was read. */
  862.         return(err);
  863.     }
  864.  
  865.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  866.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  867.  
  868.     proc = gTreeObjMethods[(*hndl)->type];
  869.     if (proc)
  870.         err = (*proc)(hndl, FREADMESSAGE, fileRefNum);
  871.     else
  872.         err = ReadTreeObjData(hndl, fileRefNum);
  873.  
  874.     if (err) return(err);
  875.  
  876.     for (cnum = 0; cnum < numChildren; ++cnum) {
  877.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  878.         if (InsertChildHndl(hndl, chndl, cnum)) {
  879.             DisposeObjAndOffspring(chndl);
  880.             return(memFullErr);
  881.         }
  882.         err = ReadBranch(chndl, fileRefNum);
  883.         if (err) return(err);
  884.     }
  885.  
  886.     return(noErr);
  887. }
  888.  
  889.  
  890.  
  891. /**********************************************************************/
  892.  
  893.  
  894.  
  895. #pragma segment TreeObj
  896. static OSErr    ReadTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  897. {
  898.     TreeObj        header;
  899.     TreeObjHndl    parent;
  900.     OSErr        err;
  901.     long        count;
  902.  
  903.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  904.     if (!(err = FSRead(fileRefNum, &count, &header))) {
  905.         parent = (*hndl)->parent;
  906.         **hndl = header;
  907.         (*hndl)->parent = parent;
  908.     }
  909.  
  910.     return(err);
  911. }
  912.  
  913.  
  914.  
  915. /**********************************************************************/
  916.  
  917.  
  918.  
  919. /* Read in dataSize number of bytes into the object. */
  920.  
  921. #pragma segment TreeObj
  922. OSErr    ReadTreeObjData(TreeObjHndl hndl, short fileRefNum)
  923. {
  924.     long    dataSize;
  925.     OSErr    err;
  926.     char    hstate;
  927.     Ptr        dataPtr;
  928.  
  929.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize))) {
  930.         hstate = HGetState((Handle)hndl);
  931.         HLock((Handle)hndl);
  932.         dataPtr = GetDataPtr(hndl);
  933.         err     = FSRead(fileRefNum, &dataSize, dataPtr);
  934.         HSetState((Handle)hndl, hstate);
  935.     }
  936.  
  937.     return(err);
  938. }
  939.  
  940.  
  941.  
  942. /**********************************************************************/
  943.  
  944.  
  945.  
  946. /* Given an open file that is ready to be written to, this function is called to
  947. ** write the file tree to the designated file. */
  948.  
  949. #pragma segment TreeObj
  950. OSErr    WriteTree(TreeObjHndl hndl, short fileRefNum)
  951. {
  952.     TreeObjProcPtr    proc;
  953.     short            cnum;
  954.     OSErr            err;
  955.  
  956.     err = WriteTreeObjHeader(hndl, fileRefNum);
  957.     if (!err) {
  958.  
  959.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  960.             /* Ready data to be written to file.  Any references to handles are invalid
  961.             ** when written to disk.  These need to be converted to a reference that
  962.             ** makes sense when read in from disk when a file is opened.  The standard
  963.             ** way to do this is to convert the handle reference to a tree-obj-number
  964.             ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  965.             ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  966.             ** giving each handle a unique id number. */
  967.  
  968.         proc = gTreeObjMethods[(*hndl)->type];
  969.         if (proc)
  970.             err = (*proc)(hndl, FWRITEMESSAGE, fileRefNum);
  971.         else
  972.             err = WriteTreeObjData(hndl, fileRefNum);
  973.  
  974.         DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  975.             /* Undo any id references back to handle references. */
  976.     }
  977.  
  978.  
  979.     if (!err) {
  980.         for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  981.             err = WriteTree(GetChildHndl(hndl, cnum), fileRefNum);
  982.             if (err) break;
  983.         }
  984.     }
  985.  
  986.     return(err);
  987. }
  988.  
  989.  
  990.  
  991. /**********************************************************************/
  992.  
  993.  
  994.  
  995. #pragma segment TreeObj
  996. static OSErr    WriteTreeObjHeader(TreeObjHndl hndl, short fileRefNum)
  997. {
  998.     TreeObj    header;
  999.     OSErr    err;
  1000.     long    count;
  1001.  
  1002.     header = **hndl;
  1003.     count = sizeof(TreeObj) - sizeof(TreeObjHndl);
  1004.     err = FSWrite(fileRefNum, &count, &header);
  1005.  
  1006.     return(err);
  1007. }
  1008.  
  1009.  
  1010.  
  1011. /**********************************************************************/
  1012.  
  1013.  
  1014.  
  1015. /* Write out dataSize number of bytes from the object. */
  1016.  
  1017. #pragma segment TreeObj
  1018. OSErr    WriteTreeObjData(TreeObjHndl hndl, short fileRefNum)
  1019. {
  1020.     long    dataSize;
  1021.     OSErr    err;
  1022.     char    hstate;
  1023.     Ptr        dataPtr;
  1024.  
  1025.     dataSize = (*hndl)->dataSize;
  1026.  
  1027.     hstate  = HGetState((Handle)hndl);
  1028.     HLock((Handle)hndl);
  1029.     dataPtr = GetDataPtr(hndl);
  1030.     err     = FSWrite(fileRefNum, &dataSize, dataPtr);
  1031.     HSetState((Handle)hndl, hstate);
  1032.  
  1033.     return(err);
  1034. }
  1035.  
  1036.  
  1037.  
  1038. /**********************************************************************/
  1039.  
  1040.  
  1041.  
  1042. /* Call the object for each member of the tree (or branch) starting
  1043. ** from the back of the tree working forward. */
  1044.  
  1045. #pragma segment TreeObj
  1046. void    DoBTreeMethod(TreeObjHndl hndl, short message, long data)
  1047. {
  1048.     short    cnum;
  1049.  
  1050.     DoTreeObjMethod(hndl, message, data);
  1051.  
  1052.     for (cnum = (*hndl)->numChildren; cnum;)
  1053.         DoBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1054. }
  1055.  
  1056.  
  1057.  
  1058. /**********************************************************************/
  1059.  
  1060.  
  1061.  
  1062. /* Same as DoBTreeMethod, except that an error aborts tree walk and returns error. */
  1063.  
  1064. #pragma segment TreeObj
  1065. OSErr    DoErrBTreeMethod(TreeObjHndl hndl, short message, long data)
  1066. {
  1067.     short    cnum;
  1068.     OSErr    err;
  1069.  
  1070.     err = DoTreeObjMethod(hndl, message, data);
  1071.     if (err) return(err);
  1072.  
  1073.     for (cnum = (*hndl)->numChildren; cnum;) {
  1074.         err = DoErrBTreeMethod(GetChildHndl(hndl, --cnum), message, data);
  1075.         if (err) return(err);
  1076.     }
  1077.  
  1078.     return(noErr);
  1079. }
  1080.  
  1081.  
  1082.  
  1083. /**********************************************************************/
  1084.  
  1085.  
  1086.  
  1087. /* Call the object for each member of the tree (or branch) starting
  1088. ** from the front of the tree working backward. */
  1089.  
  1090. #pragma segment TreeObj
  1091. void    DoFTreeMethod(TreeObjHndl hndl, short message, long data)
  1092. {
  1093.     short    cnum;
  1094.  
  1095.     DoTreeObjMethod(hndl, message, data);
  1096.  
  1097.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) 
  1098.         DoFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1099. }
  1100.  
  1101.  
  1102.  
  1103. /**********************************************************************/
  1104.  
  1105.  
  1106.  
  1107. /* Same as DoFTreeMethod, except that an error aborts tree walk and returns error. */
  1108.  
  1109. #pragma segment TreeObj
  1110. OSErr    DoErrFTreeMethod(TreeObjHndl hndl, short message, long data)
  1111. {
  1112.     short    cnum;
  1113.     OSErr    err;
  1114.  
  1115.     err = DoTreeObjMethod(hndl, message, data);
  1116.     if (err) return(err);
  1117.  
  1118.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  1119.         err = DoErrFTreeMethod(GetChildHndl(hndl, cnum), message, data);
  1120.         if (err) return(err);
  1121.     }
  1122.  
  1123.     return(noErr);
  1124. }
  1125.  
  1126.  
  1127.  
  1128. /**********************************************************************/
  1129.  
  1130.  
  1131.  
  1132. /* If the object has a method procedure, call it.  If no method procedure,
  1133. ** then do nothing. */
  1134.  
  1135. #pragma segment TreeObj
  1136. long    DoTreeObjMethod(TreeObjHndl hndl, short message, long data)
  1137. {
  1138.     TreeObjProcPtr    proc;
  1139.  
  1140.     proc = gTreeObjMethods[(*hndl)->type];
  1141.     if (proc) return((*proc)(hndl, message, data));
  1142.     return(0);
  1143. }
  1144.  
  1145.  
  1146.  
  1147. /**********************************************************************/
  1148.  
  1149.  
  1150.  
  1151. /* Number each member in the tree with a unique treeID.  The tree is number
  1152. ** sequentially from front to back.  The first treeID number is 1.  0 is
  1153. ** reserved for Hndl2ID/ID2Hndl conversions where it is possible that the
  1154. ** handle value is nil.  The nil handle will convert to 0, and convert back
  1155. ** to nil. */
  1156.  
  1157. #pragma segment TreeObj
  1158. void        DoNumberTree(TreeObjHndl hndl)
  1159. {
  1160.     DoNumberTree0(GetRootHndl(hndl));
  1161. }
  1162.  
  1163.  
  1164.  
  1165. /**********************************************************************/
  1166.  
  1167.  
  1168.  
  1169. #pragma segment TreeObj
  1170. static void    DoNumberTree0(TreeObjHndl hndl)
  1171. {
  1172.     short            cnum;
  1173.     static short    nodeNum;
  1174.  
  1175.     if (!(*hndl)->parent)
  1176.         nodeNum = 0;
  1177.     (*hndl)->treeID = ++nodeNum;
  1178.  
  1179.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum)
  1180.         DoNumberTree0(GetChildHndl(hndl, cnum));
  1181. }
  1182.  
  1183.  
  1184.  
  1185. /**********************************************************************/
  1186.  
  1187.  
  1188.  
  1189. /* This function is used to convert a handle reference into a treeID reference.
  1190. ** A pointer to the handle reference is passed in.  Typical usage will be where
  1191. ** a handle object has a reference to another handle object.  Handle object
  1192. ** references aren't meaningful when saved to disk, and therefore don't persist
  1193. ** in their native form.  These handle references need to be converted into
  1194. ** a treeID reference so that they can be saved.
  1195. ** The tree first needs to be numbered so that the treeID references are unique
  1196. ** and meaningful.  The tree is numbered by first calling DoNumberTree().  It
  1197. ** numbers all the members of the tree hierarchy uniquely and sequentially. */
  1198.  
  1199. #pragma segment TreeObj
  1200. void    Hndl2ID(TreeObjHndl *hndl)
  1201. {
  1202.     if (*hndl)
  1203.         *hndl = (TreeObjHndl)(**hndl)->treeID;
  1204. }
  1205.  
  1206.  
  1207.  
  1208. /**********************************************************************/
  1209.  
  1210.  
  1211.  
  1212. /* Given a tree object ID and a reference object (any member of the tree),
  1213. ** return the cooresponding object handle.  DoNumberTree() must be called
  1214. ** prior to using this function, and after the last change to the tree, as
  1215. ** it generates the object treeID numbers for the entire tree. */
  1216.  
  1217. #pragma segment TreeObj
  1218. void    ID2Hndl(TreeObjHndl refHndl, TreeObjHndl *hndl)
  1219. {
  1220.     short        cnum;
  1221.     TreeObjHndl    chndl;
  1222.  
  1223.     if ((!refHndl) || (!*hndl)) return;
  1224.  
  1225.     refHndl = GetRootHndl(refHndl);
  1226.     for (;;) {
  1227.         if ((*refHndl)->treeID == (long)*hndl) {
  1228.             *hndl = refHndl;
  1229.             return;
  1230.         }
  1231.         if (!(cnum = (*refHndl)->numChildren)) return;
  1232.         for (; cnum;) {
  1233.             chndl = GetChildHndl(refHndl, --cnum);
  1234.             if ((*chndl)->treeID <= (long)*hndl) {
  1235.                 refHndl = chndl;
  1236.                 break;
  1237.             }
  1238.         }
  1239.     }
  1240. }
  1241.  
  1242.  
  1243.  
  1244. /**********************************************************************/
  1245.  
  1246.  
  1247.  
  1248. /* Given an object handle, return the undo handle. */
  1249.  
  1250. #pragma segment TreeObj
  1251. TreeObjHndl    GetUndoHndl(TreeObjHndl undo)
  1252. {
  1253.     for (; (*undo)->parent; undo = (*undo)->parent) {};
  1254.     if ((*undo)->type == ROOTOBJ)
  1255.         undo = mDerefRoot(undo)->undo;
  1256.     return(undo);
  1257. }
  1258.  
  1259.  
  1260.  
  1261. /**********************************************************************/
  1262.  
  1263.  
  1264.  
  1265. /* Used to close out an old undo task.  Closing out the old task means that
  1266. ** any editing to the document will be recorded into a new undo task.  Use
  1267. ** this to separate two edits of the same edit type that would otherwise
  1268. ** get recorded into the same undo task. */
  1269.  
  1270. #pragma segment TreeObj
  1271. void    NewUndo(TreeObjHndl hndl)
  1272. {
  1273.     TreeObjHndl    undo;
  1274.  
  1275.     undo = GetUndoHndl(hndl);
  1276.     mDerefUndo(undo)->lastEditType = NO_EDIT;
  1277.     mDerefUndo(undo)->disabled     = false;
  1278. }
  1279.  
  1280.  
  1281.  
  1282. /**********************************************************************/
  1283.  
  1284.  
  1285.  
  1286. /* If an edit fails, it can be backed out of by calling this.  All edits
  1287. ** that were done will be undone, thus recovering the state of the
  1288. ** document prior to the edit. */
  1289.  
  1290. #pragma segment TreeObj
  1291. void    RevertEdit(TreeObjHndl hndl, Boolean fixup)
  1292. {
  1293.     TreeObjHndl    root;
  1294.     FileRecHndl    frHndl;
  1295.     Boolean        docDirty;
  1296.  
  1297.     root     = GetRootHndl(hndl);
  1298.     frHndl   = mDerefRoot(root)->frHndl;
  1299.     docDirty = (*frHndl)->fileState.docDirty;
  1300.  
  1301.     DoUndoTask(hndl, DOUNDO, fixup);
  1302.         /* Use the undo mechanism to back out of the edit task. */
  1303.  
  1304.     DisposeChild(NO_EDIT, GetUndoHndl(hndl), -1);
  1305.         /* Get rid of the undo that was just used to revert.
  1306.         ** Leave the rest of the undos. */
  1307.  
  1308.     (*frHndl)->fileState.docDirty = docDirty;
  1309. }
  1310.  
  1311.  
  1312.  
  1313. /**********************************************************************/
  1314.  
  1315.  
  1316.  
  1317. /* Call this to undo or redo editing to the document.  If redo is false,
  1318. ** the it is an undo task. */
  1319.  
  1320. #pragma segment TreeObj
  1321. void    DoUndoTask(TreeObjHndl hndl, Boolean redo, Boolean fixup)
  1322. {
  1323.     FileRecHndl    frHndl;
  1324.     TreeObjHndl    undo, undoTask, undoPart;
  1325.     short        numUndos, undoDepth, undoPartNum;
  1326.     short        beg, end, inc;
  1327.     Point        contOrg;
  1328.  
  1329.     NewUndo(undo = GetUndoHndl(hndl));
  1330.  
  1331.     numUndos  = (*undo)->numChildren;
  1332.     undoDepth = mDerefUndo(undo)->undoDepth;
  1333.  
  1334.     if ((redo) && (numUndos == undoDepth)) return;
  1335.     if (!(numUndos | undoDepth)) return;
  1336.  
  1337.     frHndl = mDerefUndo(undo)->frHndl;
  1338.  
  1339.     if (redo) undoDepth++;
  1340.     undoTask = GetChildHndl(undo, --undoDepth);
  1341.  
  1342.     if (!redo) {
  1343.         GetContentOrigin((*frHndl)->fileState.window, &contOrg);
  1344.         mDerefUndoTask(undoTask)->redoOrigin = contOrg;
  1345.         contOrg = mDerefUndoTask(undoTask)->undoOrigin;
  1346.     }
  1347.     else contOrg = mDerefUndoTask(undoTask)->redoOrigin;
  1348.  
  1349.     if (fixup)
  1350.         DoUndoFixup(frHndl, contOrg, 0);
  1351.             /* Prepare for undo task, such as deselecting the current selection so
  1352.             ** that the undone stuff can be displayed as the only selected stuff. */
  1353.  
  1354.     beg = (*undoTask)->numChildren - 1;
  1355.     end = -1;
  1356.     inc = -1;
  1357.     if (redo) {
  1358.         end = ++beg;
  1359.         beg = 0;
  1360.         inc = 1;
  1361.     }
  1362.  
  1363.     for (undoPartNum = beg; undoPartNum != end; undoPartNum += inc) {
  1364.         undoPart = GetChildHndl(undoTask, undoPartNum);
  1365.         switch(mDerefUndoPart(undoPart)->actionType) {
  1366.             case NEW_CHILD:
  1367.                 UndoNewChild(undoPart);
  1368.                 break;
  1369.             case DISPOSE_CHILD:
  1370.                 UndoDisposeChild(undoPart);
  1371.                 break;
  1372.             case MOVE_CHILD:
  1373.                 UndoMoveChild(undoPart);
  1374.                 break;
  1375.             case CHANGE_CHILD:
  1376.                 UndoModifyChild(undoPart);
  1377.                 break;
  1378.             case SWAP_CHILDREN:
  1379.                 UndoSwapChildren(undoPart);
  1380.                 break;
  1381.         }
  1382.     }
  1383.  
  1384.     inc = -1;
  1385.     if (redo)
  1386.         inc = 1;
  1387.  
  1388.     mDerefUndo(undo)->undoDepth += inc;
  1389.  
  1390.     if (fixup)
  1391.         DoUndoFixup(frHndl, contOrg, 1);
  1392.             /* Clean up and redisplay after undo task. */
  1393.  
  1394.     SetDocDirty(frHndl);
  1395. }
  1396.  
  1397.  
  1398.  
  1399. /**********************************************************************/
  1400.  
  1401.  
  1402.  
  1403. #pragma segment TreeObj
  1404. static TreeObjHndl    GetUndoTaskHndl(TreeObjHndl undo, short editType)
  1405. {
  1406.     TreeObjHndl        lastTaskHndl;
  1407.     short            lastEditType, undoDepth, addNewUndo, numUndoLevels, maxNumUndos;
  1408.     Point            contOrg;
  1409.     FileRecHndl        frHndl;
  1410.     WindowPtr        window;
  1411.  
  1412.     if (!(maxNumUndos = mDerefUndo(undo)->maxNumUndos)) return(nil);
  1413.  
  1414.     lastEditType = mDerefUndo(undo)->lastEditType;
  1415.     undoDepth    = mDerefUndo(undo)->undoDepth;
  1416.  
  1417.     addNewUndo = false;
  1418.     if (editType != lastEditType)
  1419.         addNewUndo = true;
  1420.     if (!(numUndoLevels = (*undo)->numChildren))
  1421.         addNewUndo = true;
  1422.  
  1423.     while (undoDepth < numUndoLevels) {
  1424.         DisposeChild(NO_EDIT, undo, --numUndoLevels);
  1425.         addNewUndo = true;        /* Flushing old also indicates a new undo. */
  1426.     }                            /* undoDepth now is the same as numUndoLevels. */
  1427.  
  1428.     lastTaskHndl = nil;
  1429.     if (!addNewUndo) {
  1430.         lastTaskHndl = GetChildHndl(undo, -1);        /* Get last child handle. */
  1431.         if ((editType) && (editType != mDerefUndoTask(lastTaskHndl)->editType))
  1432.             lastTaskHndl = nil;
  1433.     }
  1434.  
  1435.     if (!lastTaskHndl) {
  1436.         while (numUndoLevels >= maxNumUndos) {
  1437.             DisposeChild(NO_EDIT, undo, 0);
  1438.             mDerefUndo(undo)->undoDepth--;
  1439.             numUndoLevels--;
  1440.         }    /* Restrict number of undos to designated level. */
  1441.  
  1442.         lastTaskHndl = NewChild(NO_EDIT, undo, numUndoLevels, UNDOTASKOBJ, 0);
  1443.         if (lastTaskHndl) {
  1444.             mDerefUndo(undo)->lastEditType = editType;
  1445.             mDerefUndo(undo)->undoDepth++;
  1446.             frHndl = mDerefUndo(undo)->frHndl;
  1447.             window = (*frHndl)->fileState.window;
  1448.             GetContentOrigin(window, &contOrg);
  1449.             mDerefUndoTask(lastTaskHndl)->editType   = editType;
  1450.             mDerefUndoTask(lastTaskHndl)->undoOrigin = contOrg;
  1451.             mDerefUndoTask(lastTaskHndl)->redoOrigin = contOrg;
  1452.         }
  1453.     }
  1454.  
  1455.     return(lastTaskHndl);
  1456. }
  1457.  
  1458.  
  1459.  
  1460. /**********************************************************************/
  1461.  
  1462.  
  1463.  
  1464. #pragma segment TreeObj
  1465. static TreeObjHndl    NewUndoPart(TreeObjHndl taskHndl, short action, TreeObjHndl shndl, short scnum,
  1466.                                 TreeObjHndl dhndl, short dcnum, Boolean deepCopy)
  1467. {
  1468.     TreeObjHndl    partHndl;
  1469.     UndoPartObj    *partPtr;
  1470.  
  1471.     partHndl = NewChild(NO_EDIT, taskHndl, -1, UNDOPARTOBJ, 0);    /* Add new child to end. */
  1472.     if (partHndl) {
  1473.         partPtr = GetDataPtr(partHndl);
  1474.         partPtr->actionType = action;
  1475.         partPtr->shndl      = shndl;
  1476.         partPtr->scnum      = scnum;
  1477.         partPtr->dhndl      = dhndl;
  1478.         partPtr->dcnum      = dcnum;
  1479.         partPtr->deepCopy   = deepCopy;
  1480.     }
  1481.  
  1482.     return(partHndl);
  1483. }
  1484.  
  1485.  
  1486.  
  1487. /**********************************************************************/
  1488.  
  1489.  
  1490.  
  1491. #pragma segment TreeObj
  1492. static OSErr    PostNewChild(short editType, TreeObjHndl phndl, short cnum)
  1493. {
  1494.     TreeObjHndl    undo, task;
  1495.  
  1496.     if (!editType) return(noErr);
  1497.  
  1498.     undo = GetUndoHndl(phndl);
  1499.     if (mDerefUndo(undo)->disabled) return(noErr);
  1500.  
  1501.     task = GetUndoTaskHndl(undo, editType);
  1502.     if (task)
  1503.         if (NewUndoPart(task, NEW_CHILD, phndl, cnum, nil, 0, false)) return(noErr);
  1504.  
  1505.     return(memFullErr);
  1506. }
  1507.  
  1508.  
  1509.  
  1510. /**********************************************************************/
  1511.  
  1512.  
  1513.  
  1514. #pragma segment TreeObj
  1515. static OSErr    PostDisposeChild(short editType, TreeObjHndl phndl, short cnum)
  1516. {
  1517.     TreeObjHndl    undo, task, part;
  1518.  
  1519.     if (!editType) return(noErr);
  1520.  
  1521.     undo = GetUndoHndl(phndl);
  1522.     if (mDerefUndo(undo)->disabled) return(noErr);
  1523.  
  1524.     task = GetUndoTaskHndl(undo, editType);
  1525.     if (task) {
  1526.         part = NewUndoPart(task, DISPOSE_CHILD, phndl, cnum, nil, 0, false);
  1527.         if (part) {
  1528.             MoveChild(NO_EDIT, phndl, cnum, part, -1);
  1529.             return(noErr);
  1530.         }
  1531.     }
  1532.  
  1533.     return(memFullErr);
  1534. }
  1535.  
  1536.  
  1537.  
  1538. /**********************************************************************/
  1539.  
  1540.  
  1541.  
  1542. #pragma segment TreeObj
  1543. static void    PostMoveChild(short editType, TreeObjHndl shndl, short scnum, TreeObjHndl dhndl, short dcnum)
  1544. {
  1545.     TreeObjHndl    undo, task;
  1546.  
  1547.     if (editType) {
  1548.         undo = GetUndoHndl(shndl);
  1549.         if (!mDerefUndo(undo)->disabled) {
  1550.             task = GetUndoTaskHndl(undo, editType);
  1551.             if (task) {
  1552.                 NewUndoPart(task, MOVE_CHILD, shndl, scnum, dhndl, dcnum, false);
  1553.             }
  1554.         }
  1555.     }
  1556. }
  1557.  
  1558.  
  1559.  
  1560. /**********************************************************************/
  1561.  
  1562.  
  1563.  
  1564. #pragma segment TreeObj
  1565. static void    PostSwapChildren(short editType, TreeObjHndl hndla, short cnuma, TreeObjHndl hndlb, short cnumb)
  1566. {
  1567.     TreeObjHndl    undo, task;
  1568.  
  1569.     if (editType) {
  1570.         undo = GetUndoHndl(hndla);
  1571.         if (!mDerefUndo(undo)->disabled) {
  1572.             task = GetUndoTaskHndl(undo, editType);
  1573.             if (task) {
  1574.                 NewUndoPart(task, SWAP_CHILDREN, hndla, cnuma, hndlb, cnumb, false);
  1575.             }
  1576.         }
  1577.     }
  1578. }
  1579.  
  1580.  
  1581.  
  1582. /**********************************************************************/
  1583.  
  1584.  
  1585.  
  1586. #pragma segment TreeObj
  1587. static void    UndoNewChild(TreeObjHndl undoPart)
  1588. {
  1589.     TreeObjHndl    shndl, chndl;
  1590.     short        scnum;
  1591.  
  1592.     shndl = mDerefUndoPart(undoPart)->shndl;
  1593.     scnum = mDerefUndoPart(undoPart)->scnum;
  1594.     mDerefUndoPart(undoPart)->actionType = DISPOSE_CHILD;
  1595.  
  1596.     chndl = GetChildHndl(shndl, scnum);
  1597.     MoveChild(NO_EDIT, shndl, scnum, undoPart, 0);
  1598. }
  1599.  
  1600.  
  1601.  
  1602. /**********************************************************************/
  1603.  
  1604.  
  1605.  
  1606. #pragma segment TreeObj
  1607. static void    UndoDisposeChild(TreeObjHndl undoPart)
  1608. {
  1609.     TreeObjHndl    shndl, chndl;
  1610.     short        scnum;
  1611.  
  1612.     shndl = mDerefUndoPart(undoPart)->shndl;
  1613.     scnum = mDerefUndoPart(undoPart)->scnum;
  1614.     mDerefUndoPart(undoPart)->actionType = NEW_CHILD;
  1615.  
  1616.     chndl = GetChildHndl(undoPart, 0);
  1617.     MoveChild(NO_EDIT, undoPart, 0, shndl, scnum);
  1618. }
  1619.  
  1620.  
  1621.  
  1622. /**********************************************************************/
  1623.  
  1624.  
  1625.  
  1626. #pragma segment TreeObj
  1627. static void    UndoMoveChild(TreeObjHndl undoPart)
  1628. {
  1629.     TreeObjHndl    shndl, dhndl;
  1630.     short        scnum, dcnum;
  1631.  
  1632.     shndl = mDerefUndoPart(undoPart)->shndl;
  1633.     scnum = mDerefUndoPart(undoPart)->scnum;
  1634.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1635.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1636.  
  1637.     MoveChild(NO_EDIT, dhndl, dcnum, shndl, scnum);
  1638.  
  1639.     mDerefUndoPart(undoPart)->shndl = dhndl;
  1640.     mDerefUndoPart(undoPart)->scnum = dcnum;
  1641.     mDerefUndoPart(undoPart)->dhndl = shndl;
  1642.     mDerefUndoPart(undoPart)->dcnum = scnum;
  1643. }
  1644.  
  1645.  
  1646.  
  1647. /**********************************************************************/
  1648.  
  1649.  
  1650.  
  1651. #pragma segment TreeObj
  1652. static void    UndoModifyChild(TreeObjHndl undoPart)
  1653. {
  1654.     TreeObjHndl    shndl, dchndl, uchndl;
  1655.     short        scnum;
  1656.  
  1657.     shndl = mDerefUndoPart(undoPart)->shndl;
  1658.     scnum = mDerefUndoPart(undoPart)->scnum;
  1659.  
  1660.     dchndl = GetChildHndl(shndl, scnum);    /* Document child handle. */
  1661.     uchndl = GetChildHndl(undoPart, 0);        /* Undo child handle. */
  1662.  
  1663.     UndoModifyChildren(dchndl, uchndl, mDerefUndoPart(undoPart)->deepCopy);
  1664. }
  1665.  
  1666.  
  1667.  
  1668. /**********************************************************************/
  1669.  
  1670.  
  1671.  
  1672. #pragma segment TreeObj
  1673. static void    UndoModifyChildren(TreeObjHndl dhndl, TreeObjHndl uhndl, Boolean deepCopy)
  1674. {
  1675.     TreeObjHndl    dchndl, uchndl;
  1676.     short        i;
  1677.  
  1678.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOFROMDOC);    /* Old data leaving document. */
  1679.     SwapTreeObjData(dhndl, uhndl);
  1680.     DoTreeObjMethod(dhndl, UNDOMESSAGE, UNDOTODOC);        /* New data entering document. */
  1681.  
  1682.     if (deepCopy) {
  1683.         for (i = (*dhndl)->numChildren; i;) {
  1684.             dchndl = GetChildHndl(dhndl, --i);
  1685.             uchndl = GetChildHndl(uhndl, i);
  1686.             UndoModifyChildren(dchndl, uchndl, deepCopy);
  1687.         }
  1688.     }
  1689. }
  1690.  
  1691.  
  1692.  
  1693. /**********************************************************************/
  1694.  
  1695.  
  1696.  
  1697. #pragma segment TreeObj
  1698. static void    UndoSwapChildren(TreeObjHndl undoPart)
  1699. {
  1700.     TreeObjHndl    shndl, dhndl, schndl, dchndl;
  1701.     short        scnum, dcnum;
  1702.  
  1703.     shndl = mDerefUndoPart(undoPart)->shndl;
  1704.     scnum = mDerefUndoPart(undoPart)->scnum;
  1705.     dhndl = mDerefUndoPart(undoPart)->dhndl;
  1706.     dcnum = mDerefUndoPart(undoPart)->dcnum;
  1707.  
  1708.     schndl = GetChildHndl(shndl, scnum);
  1709.     dchndl = GetChildHndl(dhndl, dcnum);
  1710.  
  1711.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOFROMDOC);
  1712.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOFROMDOC);
  1713.  
  1714.     SwapChildren(NO_EDIT, shndl, scnum, dhndl, dcnum);
  1715.  
  1716.     DoTreeObjMethod(schndl, UNDOMESSAGE, UNDOTODOC);
  1717.     DoTreeObjMethod(dchndl, UNDOMESSAGE, UNDOTODOC);
  1718. }
  1719.  
  1720.  
  1721.  
  1722. /**********************************************************************/
  1723.  
  1724.  
  1725.  
  1726. /* Dispose of all undo information and prevent further undo collection.
  1727. ** Calling NewUndo() will re-enable undo collection. */
  1728.  
  1729. #pragma segment TreeObj
  1730. void    DisableUndo(TreeObjHndl hndl)
  1731. {
  1732.     TreeObjHndl    undo;
  1733.  
  1734.     undo = GetUndoHndl(hndl);
  1735.     while ((*undo)->numChildren) DisposeChild(NO_EDIT, undo, 0);
  1736.  
  1737.     mDerefUndo(undo)->undoDepth = 0;
  1738.     mDerefUndo(undo)->disabled  = true;
  1739. }
  1740.  
  1741.  
  1742.  
  1743. /**********************************************************************/
  1744.  
  1745.  
  1746.  
  1747. /* Dispose of all undo information and prevent further undo collection.
  1748. ** Calling NewUndo() will re-enable undo collection. */
  1749.  
  1750. #pragma segment TreeObj
  1751. void    DisposeUndos(TreeObjHndl hndl)
  1752. {
  1753.     DisableUndo(hndl);
  1754.     NewUndo(hndl);
  1755. }
  1756.  
  1757.  
  1758.  
  1759. /**********************************************************************/
  1760.  
  1761.  
  1762.  
  1763. /* Dispose of all undo information except the current undo.  The current undo
  1764. ** may still be active, and it may be needed to back out of an edit operation. */
  1765.  
  1766. #pragma segment TreeObj
  1767. Boolean    PurgeUndo(TreeObjHndl hndl)
  1768. {
  1769.     TreeObjHndl    undo;
  1770.     Boolean        didPurge;
  1771.  
  1772.     undo = GetUndoHndl(hndl);
  1773.     didPurge = false;
  1774.     while ((*undo)->numChildren > 1) {
  1775.         DisposeChild(NO_EDIT, undo, 0);
  1776.         if (mDerefUndo(undo)->undoDepth) --mDerefUndo(undo)->undoDepth;
  1777.         didPurge = true;
  1778.     }
  1779.  
  1780.     return(didPurge);
  1781. }
  1782.  
  1783.  
  1784.  
  1785. /**********************************************************************/
  1786.  
  1787.  
  1788.  
  1789. #pragma segment TreeObj
  1790. void    GetUndoInfo(FileRecHndl frHndl, short *undoDepth, short *numUndos)
  1791. {
  1792.     TreeObjHndl    undo;
  1793.  
  1794.     *undoDepth = *numUndos = 0;
  1795.     if ((*frHndl)->fileState.readOnly) return;
  1796.  
  1797.     undo = GetUndoHndl((*frHndl)->d.doc.root);
  1798.     if (!mDerefUndo(undo)->disabled) {
  1799.         *undoDepth = mDerefUndo(undo)->undoDepth;
  1800.         *numUndos  = (*undo)->numChildren;
  1801.     }
  1802. }
  1803.  
  1804.  
  1805.  
  1806. /*****************************************************************************/
  1807. /*****************************************************************************/
  1808.  
  1809.  
  1810.  
  1811. /* This function does the standard document initialization. */
  1812.  
  1813. #pragma segment TreeObj
  1814. OSErr    DefaultInitDocument(FileRecHndl frHndl, short version, short numUndos, short numSaveUndos)
  1815. {
  1816.     TreeObjHndl    root, undo;
  1817.     OSErr        err;
  1818.  
  1819.     err = noErr;
  1820.  
  1821.     (*frHndl)->d.doc.fhInfo.version = version;
  1822.  
  1823.     (*frHndl)->fileState.readDocumentHeaderProc  = DefaultReadDocumentHeader;
  1824.     (*frHndl)->fileState.writeDocumentHeaderProc = DefaultWriteDocumentHeader;
  1825.  
  1826.     root = NewRootObj(ROOTOBJ, 0);
  1827.     if (root) {                                    /* Create hierarchical data root. */
  1828.         (*frHndl)->d.doc.root = root;            /* Link file to hierarchical data. */
  1829.         undo = NewRootObj(UNDOOBJ, 0);            /* Create hierarchical undo root. */
  1830.         mDerefRoot(root)->undo    = undo;        /* Save hierarchical undo root. */
  1831.         mDerefRoot(root)->frHndl  = frHndl;
  1832.         if (undo) {
  1833.             (*frHndl)->fileState.defaultDoc = true;
  1834.             mDerefUndo(undo)->root   = root;    /* Point undo back at file root. */
  1835.             mDerefUndo(undo)->frHndl = frHndl;
  1836.             mDerefUndo(undo)->maxNumUndos  = numUndos;
  1837.             mDerefUndo(undo)->numSaveUndos = numSaveUndos;
  1838.         }
  1839.         else {
  1840.             DefaultFreeDocument(frHndl);
  1841.             err = memFullErr;
  1842.         }
  1843.     }
  1844.     else err = memFullErr;
  1845.  
  1846.     return(err);
  1847. }
  1848.  
  1849.  
  1850.  
  1851. /*****************************************************************************/
  1852.  
  1853.  
  1854.  
  1855. /* Frees up the hierarchical document and undo portions of a default document. */
  1856.  
  1857. #pragma segment TreeObj
  1858. OSErr    DefaultFreeDocument(FileRecHndl frHndl)
  1859. {
  1860.     TreeObjHndl    root, undo;
  1861.  
  1862.     root = (*frHndl)->d.doc.root;
  1863.     if (root) {                                    /* If we have a valid root object... */
  1864.         undo = mDerefRoot(root)->undo;
  1865.         if (undo)                                /* If we have a valid undo object... */
  1866.             DisposeObjAndOffspring(undo);        /* Dispose of undo info. */
  1867.         DisposeObjAndOffspring(root);            /* Dispose of hierarchical file data. */
  1868.         (*frHndl)->d.doc.root = nil;
  1869.     }
  1870.     return(noErr);
  1871. }
  1872.  
  1873.  
  1874.  
  1875. /*****************************************************************************/
  1876.  
  1877.  
  1878.  
  1879. #pragma segment TreeObj
  1880. OSErr    DefaultReadDocument(FileRecHndl frHndl)
  1881. {
  1882.     OSErr        err;
  1883.     TreeObjHndl    root, undo;
  1884.     short        resID, refNum, flags;
  1885.     Movie        movie;
  1886.     Boolean        wasChanged;
  1887.  
  1888.     refNum = (*frHndl)->fileState.refNum;
  1889.     err    = SetFPos(refNum, fsFromStart, 0);
  1890.         /* Set the file position to the beginning of the file. */
  1891.  
  1892.     err = DoReadDocumentHeader(frHndl);
  1893.  
  1894.     if (!err) {
  1895.         if ((*frHndl)->fileState.sfType == MovieFileType) {
  1896.             resID  = 0;
  1897.             flags  = (*frHndl)->fileState.movieFlags;
  1898.             err = NewMovieFromFile(&movie, refNum, &resID, nil, flags, &wasChanged);
  1899.             if (err) return(err);
  1900.             (*frHndl)->fileState.movie                  = movie;
  1901.             (*frHndl)->fileState.movieResID             = resID;
  1902.             (*frHndl)->fileState.movieDataRefWasChanged = wasChanged;
  1903.         }
  1904.         else {
  1905.             root = (*frHndl)->d.doc.root;        /* Preserve the undo field.  Preserving  */
  1906.             undo = mDerefRoot(root)->undo;        /* any application-sepcific fields is up */
  1907.                                                 /* to the application.                     */
  1908.             err = ReadTree(root, refNum);
  1909.                 /* Read in the hierarchical file data portion. */
  1910.  
  1911.             mDerefRoot(root)->undo   = undo;    /* Restore the 2 over-written fields. */
  1912.             mDerefRoot(root)->frHndl = frHndl;
  1913.         }
  1914.     }
  1915.  
  1916.     return(err);
  1917. }
  1918.  
  1919.  
  1920.  
  1921. /*****************************************************************************/
  1922.  
  1923.  
  1924.  
  1925. #pragma segment TreeObj
  1926. OSErr    DefaultReadDocumentFixup(FileRecHndl frHndl)
  1927. {
  1928.     TreeObjHndl    root, undo, chndl;
  1929.  
  1930.     root = (*frHndl)->d.doc.root;
  1931.     undo = mDerefRoot(root)->undo;
  1932.     for (;;) {
  1933.         if (!(chndl = GetChildHndl(root, -1))) break;
  1934.         if ((*chndl)->type != UNDOTASKOBJ)     break;
  1935.         MoveChild(NO_EDIT, root, -1, undo, -1);
  1936.         mDerefUndo(undo)->undoDepth++;
  1937.             /* Move any undo tasks that were saved with the document
  1938.             ** out of the document and into the undo hierarchy. */
  1939.     }
  1940.  
  1941.     return(noErr);
  1942. }
  1943.  
  1944.  
  1945.  
  1946. /*****************************************************************************/
  1947.  
  1948.  
  1949.  
  1950. #pragma segment TreeObj
  1951. OSErr    DefaultWriteDocument(FileRecHndl frHndl)
  1952. {
  1953.     short        refNum, cnum, numSaveUndos;
  1954.     OSErr        err;
  1955.     long        fpos;
  1956.     TreeObjHndl    root, undo, chndl;
  1957.  
  1958.     refNum = (*frHndl)->fileState.refNum;
  1959.     err    = DoWriteDocumentHeader(frHndl);
  1960.  
  1961.     if (!err) {
  1962.         if ((*frHndl)->fileState.sfType != MovieFileType) {
  1963.             undo         = GetUndoHndl(root = (*frHndl)->d.doc.root);
  1964.             numSaveUndos = mDerefUndo(undo)->numSaveUndos;
  1965.             for (cnum = mDerefUndo(undo)->undoDepth; ((cnum) && (numSaveUndos)); --numSaveUndos)
  1966.                 MoveChild(NO_EDIT, undo, --cnum, root, -1);
  1967.                     /* Move the designated number of undo tasks into the document side.
  1968.                     ** They will be saved to disk this way.  The designated number may be
  1969.                     ** zero, which means that undos aren't saved to disk. */
  1970.  
  1971.             DoNumberTree(root);
  1972.                 /* Assign each object in the tree a unique treeID.  This will allow
  1973.                 ** the objects to convert handle references into ID references so that
  1974.                 ** the data can be saved to disk. */
  1975.  
  1976.             err = WriteTree(root, refNum);
  1977.                 /* Write out the hierarchical data.  This includes the data in the root object.
  1978.                 ** When reading a data file, the root object data has already been initialized,
  1979.                 ** and therefore reading in the old root data from disk is a bad thing.  This is
  1980.                 ** handled by caching the root data prior to reading in a file, and then
  1981.                 ** restoring the data after the ReadTree() call has been made. */
  1982.  
  1983.             for (;;) {
  1984.                 if (!(chndl = GetChildHndl(root, -1))) break;
  1985.                 if ((*chndl)->type != UNDOTASKOBJ)     break;
  1986.                 MoveChild(NO_EDIT, root, -1, undo, cnum++);
  1987.                     /* Move any undo tasks that we previously moved into the document
  1988.                     ** out of the document and back into the undo hierarchy. */
  1989.             }
  1990.  
  1991.             if (!err) {
  1992.                 err = GetFPos(refNum, &fpos);
  1993.                 if (!err)
  1994.                     err = SetEOF(refNum, fpos);
  1995.             }            /* The document may be shorter than last time it was written to disk.
  1996.                         ** Handle this case by ending the file based on the new length. */
  1997.         }
  1998.         else {
  1999.         }
  2000.     }
  2001.  
  2002.     return(err);
  2003. }
  2004.  
  2005.  
  2006.  
  2007. /**********************************************************************/
  2008. /**********************************************************************/
  2009. /**********************************************************************/
  2010.  
  2011.  
  2012.  
  2013. #pragma segment TreeObj
  2014. OSErr    HReadTree(TreeObjHndl hndl, Handle tree)
  2015. {
  2016.     OSErr    err;
  2017.  
  2018.     if (!GetHandleSize(tree)) return(noErr);
  2019.  
  2020.     err = HReadBranch(hndl, tree);
  2021.     if (err) {
  2022.         while ((*hndl)->numChildren)
  2023.             DisposeChild(NO_EDIT, hndl, 0);
  2024.     }
  2025.     else {
  2026.         DoNumberTree(hndl);
  2027.         DoFTreeMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2028.     }
  2029.  
  2030.     return(err);
  2031. }
  2032.  
  2033.  
  2034.  
  2035. /**********************************************************************/
  2036.  
  2037.  
  2038.  
  2039. /* This function recursively dissects the handle into separate tree objects.  The handle
  2040. ** has previously had tree objects streamed into it.  The public format is as follows:
  2041. ** 1) object header
  2042. ** 2) data length (4 bytes)
  2043. ** 3) data
  2044. **
  2045. ** After the object is created, the header is moved into it.  Then the data length is
  2046. ** fetched and a data handle is created to hold the data portion of the streamed data
  2047. ** for this object.  Once the data handle holds the object data, the tree handle has
  2048. ** the information for this object removed from it.
  2049. ** After this data separation, we call the object and pass it the data handle.
  2050. ** The object is responsible for interpreting the handle and initializing the data
  2051. ** portion of the object with it. */
  2052.  
  2053. #pragma segment TreeObj
  2054. OSErr    HReadBranch(TreeObjHndl hndl, Handle tree)
  2055. {
  2056.     TreeObjHndl        chndl;
  2057.     Handle            treeObjData;
  2058.     TreeObjProcPtr    proc;
  2059.     short            numChildren, cnum;
  2060.     long            size, diff;
  2061.     OSErr            err;
  2062.  
  2063.     if (GetHandleSize(tree) < sizeof(TreeObj)) return(paramErr);
  2064.  
  2065.     BlockMove(*tree, *hndl, sizeof(TreeObj) - sizeof(TreeObjHndl));
  2066.  
  2067.     numChildren = (*hndl)->numChildren;    /* So we can dispose of a partial read if */
  2068.     (*hndl)->numChildren = 0;            /* we have a failure after this point.    */
  2069.  
  2070.     BlockMove((long *)(*tree + sizeof(TreeObj) - sizeof(TreeObjHndl)), (Ptr)&size, sizeof(size));
  2071.         /* Get the size of the data following the header data. */
  2072.  
  2073.     if (!(treeObjData = NewHandle(size)))
  2074.         return(memFullErr);
  2075.             /* Create a handle the size of the data. */
  2076.  
  2077.     BlockMove(*tree + sizeof(TreeObj), *treeObjData, size);
  2078.         /* Copy the data into the handle. */
  2079.  
  2080.     diff = sizeof(TreeObj) + size;
  2081.     size = GetHandleSize(tree) - diff;
  2082.         /* Calculate how much we are going to shrink the tree handle. */
  2083.  
  2084.     BlockMove(*tree + diff, *tree, size);
  2085.     SetHandleSize(tree, size);
  2086.  
  2087.     proc = gTreeObjMethods[(*hndl)->type];
  2088.     if (proc)
  2089.         err = (*proc)(hndl, HREADMESSAGE, (long)treeObjData);
  2090.     else
  2091.         err = HReadTreeObjData(hndl, treeObjData);
  2092.  
  2093.     DisposeHandle(treeObjData);
  2094.  
  2095.     if (err)
  2096.         return(err);
  2097.  
  2098.     for (cnum = 0; cnum < numChildren; ++cnum) {
  2099.         if (!(chndl = NewRootObj(EMPTYOBJ, 0))) return(memFullErr);
  2100.         if (InsertChildHndl(hndl, chndl, cnum)) {
  2101.             DisposeObjAndOffspring(chndl);
  2102.             return(memFullErr);
  2103.         }
  2104.         err = HReadBranch(chndl, tree);
  2105.         if (err) return(err);
  2106.     }
  2107.  
  2108.     return(noErr);
  2109. }
  2110.  
  2111.  
  2112.  
  2113. /**********************************************************************/
  2114.  
  2115.  
  2116.  
  2117. /* The simple handle read can assume that there will be no expansion or interpretation
  2118. ** of the data.  This means that the dataSize field represents the correct data size. */
  2119.  
  2120. #pragma segment TreeObj
  2121. OSErr    HReadTreeObjData(TreeObjHndl hndl, Handle treeObjData)
  2122. {
  2123.     long    dataSize;
  2124.     OSErr    err;
  2125.  
  2126.     if (!(err = SetDataSize(hndl, dataSize = (*hndl)->dataSize)))
  2127.         BlockMove(*treeObjData, (Ptr)(*hndl + 1), dataSize);
  2128.  
  2129.     return(err);
  2130. }
  2131.  
  2132.  
  2133.  
  2134. /**********************************************************************/
  2135.  
  2136.  
  2137.  
  2138. /* Given an open file that is ready to be written to, this function is called to
  2139. ** write the file tree to the designated file. */
  2140.  
  2141. #pragma segment TreeObj
  2142. OSErr    HWriteTree(TreeObjHndl hndl, Handle tree)
  2143. {
  2144.     TreeObjProcPtr    proc;
  2145.     Handle            data;
  2146.     short            cnum;
  2147.     OSErr            err;
  2148.     long            tsize, dsize;
  2149.  
  2150.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOID);
  2151.         /* Ready data to be written to file.  Any references to handles are invalid
  2152.         ** when written to disk.  These need to be converted to a reference that
  2153.         ** makes sense when read in from disk when a file is opened.  The standard
  2154.         ** way to do this is to convert the handle reference to a tree-obj-number
  2155.         ** reference.  Prior to WriteTree() being called, DoNumberTree() is called.
  2156.         ** DoNumberTree() walks the tree and numbers each handle sequentially, thus
  2157.         ** giving each handle a unique id number. */
  2158.  
  2159.     if (!(data = NewHandle((*hndl)->dataSize)))
  2160.         return(memFullErr);
  2161.  
  2162.     proc = gTreeObjMethods[(*hndl)->type];
  2163.     if (proc)
  2164.         err = (*proc)(hndl, HWRITEMESSAGE, (long)data);
  2165.     else
  2166.         err = HWriteTreeObjData(hndl, data);
  2167.  
  2168.     if (err) {
  2169.         DisposeHandle(data);
  2170.         return(err);
  2171.     }
  2172.  
  2173.     tsize = GetHandleSize(tree);
  2174.     dsize = GetHandleSize(data);
  2175.     SetHandleSize(tree, tsize + sizeof(TreeObj) + dsize);
  2176.     BlockMove((Ptr)*hndl,  (*tree + tsize), sizeof(TreeObj) - sizeof(TreeObjHndl));
  2177.     BlockMove((Ptr)&dsize, (*tree + tsize + sizeof(TreeObj) - sizeof(TreeObjHndl)), sizeof(long));
  2178.     BlockMove(*data,       (*tree + tsize + sizeof(TreeObj)), dsize);
  2179.  
  2180.     DisposeHandle(data);
  2181.     DoTreeObjMethod(hndl, CONVERTMESSAGE, CONVERTTOHNDL);
  2182.         /* Undo any id references back to handle references. */
  2183.  
  2184.     for (cnum = 0; cnum < (*hndl)->numChildren; ++cnum) {
  2185.         err = HWriteTree(GetChildHndl(hndl, cnum), tree);
  2186.         if (err) break;
  2187.     }
  2188.  
  2189.     return(err);
  2190. }
  2191.  
  2192.  
  2193.  
  2194. /**********************************************************************/
  2195.  
  2196.  
  2197.  
  2198. /* Write out dataSize number of bytes from the object. */
  2199.  
  2200. #pragma segment TreeObj
  2201. OSErr    HWriteTreeObjData(TreeObjHndl hndl, Handle data)
  2202. {
  2203.     long    dataSize;
  2204.     OSErr    err;
  2205.  
  2206.     SetHandleSize(data, dataSize = (*hndl)->dataSize);
  2207.     err = MemError();
  2208.     if (err) return(err);
  2209.  
  2210.     BlockMove((Ptr)(*hndl + 1), *data, dataSize);
  2211.     return(noErr);
  2212. }
  2213.  
  2214.  
  2215.  
  2216. /**********************************************************************/
  2217. /**********************************************************************/
  2218.  
  2219.  
  2220.  
  2221. #pragma segment TreeObj
  2222. long    GetCData(TreeObjHndl hndl, long offset, char *data)
  2223. {
  2224.     Ptr        dptr;
  2225.     long    len;
  2226.  
  2227.     dptr = (Ptr)GetDataPtr(hndl) + offset;
  2228.     len  = clen(dptr);
  2229.  
  2230.     if (data)
  2231.         ccpy(data, dptr);
  2232.  
  2233.     return(len);
  2234. }
  2235.  
  2236.  
  2237.  
  2238. /**********************************************************************/
  2239.  
  2240.  
  2241.  
  2242. #pragma segment TreeObj
  2243. OSErr    PutCData(TreeObjHndl hndl, long offset, char *data)
  2244. {
  2245.     unsigned char    *dptr;
  2246.     long            oldl, newl;
  2247.     OSErr            err;
  2248.  
  2249.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2250.  
  2251.     oldl = clen((Ptr)dptr) + 1;
  2252.     newl = clen(data) + 1;
  2253.  
  2254.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2255.     if (err) return(err);
  2256.  
  2257.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2258.     ccpy((Ptr)dptr, data);
  2259.  
  2260.     return(noErr);
  2261. }
  2262.  
  2263.  
  2264.  
  2265. /**********************************************************************/
  2266.  
  2267.  
  2268.  
  2269. #pragma segment TreeObj
  2270. void    GetPData(TreeObjHndl hndl, long offset, StringPtr data)
  2271. {
  2272.     StringPtr    dptr;
  2273.  
  2274.     dptr = (StringPtr)GetDataPtr(hndl) + offset;
  2275.     pcpy(data, dptr);
  2276. }
  2277.  
  2278.  
  2279.  
  2280. /**********************************************************************/
  2281.  
  2282.  
  2283.  
  2284. #pragma segment TreeObj
  2285. OSErr    PutPData(TreeObjHndl hndl, long offset, StringPtr data)
  2286. {
  2287.     unsigned char    *dptr;
  2288.     long            oldl, newl;
  2289.     OSErr            err;
  2290.  
  2291.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2292.  
  2293.     oldl = *dptr + 1;
  2294.     newl = (*(unsigned char *)data) + 1;
  2295.  
  2296.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2297.     if (err) return(err);
  2298.  
  2299.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2300.     pcpy(dptr, data);
  2301.  
  2302.     return(noErr);
  2303. }
  2304.  
  2305.  
  2306.  
  2307. /**********************************************************************/
  2308.  
  2309.  
  2310.  
  2311. #pragma segment TreeObj
  2312. OSErr    PutShortData(TreeObjHndl hndl, long offset, void *data, unsigned short size)
  2313. {
  2314.     unsigned short    *dptr;
  2315.     long            oldl, newl;
  2316.     short            ss;
  2317.     OSErr            err;
  2318.  
  2319.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2320.  
  2321.     BlockMove(dptr, &ss, sizeof(short));
  2322.     oldl = ss    + sizeof(short);
  2323.     newl = size  + sizeof(short);
  2324.  
  2325.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2326.     if (err) return(err);
  2327.  
  2328.     dptr = (unsigned short *)((unsigned char *)GetDataPtr(hndl) + offset);
  2329.     BlockMove(&size, dptr, sizeof(short));
  2330.     BlockMove(data, dptr + 1, size);
  2331.  
  2332.     return(noErr);
  2333. }
  2334.  
  2335.  
  2336.  
  2337. /**********************************************************************/
  2338.  
  2339.  
  2340.  
  2341. #pragma segment TreeObj
  2342. OSErr    PutLongData(TreeObjHndl hndl, long offset, void *data, long size)
  2343. {
  2344.     unsigned long    *dptr, ll;
  2345.     long            oldl, newl;
  2346.     OSErr            err;
  2347.  
  2348.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2349.  
  2350.     BlockMove(dptr, &ll, sizeof(long));
  2351.     oldl = ll    + sizeof(long);
  2352.     newl = size  + sizeof(long);
  2353.  
  2354.     err = SlideData(hndl, offset + oldl, newl - oldl);
  2355.     if (err) return(err);
  2356.  
  2357.     dptr = (unsigned long *)((unsigned char *)GetDataPtr(hndl) + offset);
  2358.     BlockMove(&size, dptr, sizeof(long));
  2359.     BlockMove(data, dptr + 1, size);
  2360.  
  2361.     return(noErr);
  2362. }
  2363.  
  2364.  
  2365.  
  2366. /**********************************************************************/
  2367.  
  2368.  
  2369.  
  2370. #pragma segment TreeObj
  2371. unsigned long    GetDataOffset(TreeObjHndl hndl, unsigned long offset, short dtype, short dnum)
  2372. {
  2373.     unsigned char    *dptr;
  2374.     short            ss;
  2375.     long            ll;
  2376.  
  2377.     while (dnum--) {
  2378.     dptr = (unsigned char *)GetDataPtr(hndl) + offset;
  2379.         switch (dtype) {
  2380.             case kCStr:
  2381.                 offset += clen((Ptr)dptr) + 1;
  2382.                 break;
  2383.             case kPStr:
  2384.                 offset += *dptr + 1;
  2385.                 break;
  2386.             case kSDataBlock:
  2387.                 BlockMove(dptr, &ss, sizeof(short));
  2388.                 offset += ss + sizeof(short);
  2389.                 break;
  2390.             case kLDataBlock:
  2391.                 BlockMove(dptr, &ll, sizeof(long));
  2392.                 offset += ll + sizeof(long);
  2393.                 break;
  2394.         }
  2395.     }
  2396.  
  2397.     return(offset);
  2398. }
  2399.  
  2400.  
  2401.  
  2402. /**********************************************************************/
  2403. /**********************************************************************/
  2404.  
  2405.  
  2406.  
  2407. #pragma segment TreeObj
  2408. Boolean    EqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2409. {
  2410.     TreeObjProcPtr    proc;
  2411.  
  2412.     if ((h1) && (!h2)) return(false);
  2413.     if ((h2) && (!h1)) return(false);
  2414.  
  2415.     if ((*h1)->type != (*h2)->type) return(false);
  2416.  
  2417.     proc = gTreeObjMethods[(*h1)->type];
  2418.     if (proc)
  2419.         return((*proc)(h1, COMPAREMESSAGE, (long)h2));
  2420.     else
  2421.         return(DefaultEqualTreeObjData(h1, h2));
  2422. }
  2423.  
  2424.  
  2425.  
  2426. /**********************************************************************/
  2427.  
  2428.  
  2429.  
  2430. #pragma segment TreeObj
  2431. Boolean    DefaultEqualTreeObjData(TreeObjHndl h1, TreeObjHndl h2)
  2432. {
  2433.     Ptr        p1, p2;
  2434.     long    ii, jj;
  2435.  
  2436.     if ((jj = (*h1)->dataSize) != (*h2)->dataSize) return(false);
  2437.  
  2438.     p1 = GetDataPtr(h1);
  2439.     p2 = GetDataPtr(h2);
  2440.  
  2441.     for (ii = 0; ii < jj; ++ii) {
  2442.         if (p1[ii] != p2[ii]) return(false);
  2443.     }
  2444.  
  2445.     return(true);
  2446. }
  2447.  
  2448.  
  2449.  
  2450.